#include "sdkconfig.h"
#include "driver/gpio.h"
#if CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
#include "esp_vfs_semihost.h"
#include "esp_vfs_fat.h"
#include "esp_ota_ops.h"
#include "esp_app_desc.h"
#include "esp_spiffs.h"
#include "sdmmc_cmd.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_sleep.h"
#include "esp_littlefs.h"
#include "nvs.h"
#include "esp_log.h"
#include "mdns.h"
#include "lwip/apps/netbiosns.h"
#include "protocol_examples_common.h"
#if CONFIG_EXAMPLE_WEB_DEPLOY_SD
#include "driver/sdmmc_host.h"
#endif

#define MDNS_HOST_NAME "esp_toolbox"
#define MDNS_INSTANCE "esp wireless toolbox"
#define MOUNT_FS_PATH "/www"

static const char *TAG = "main";

static void initialise_mdns(void)
{
    mdns_init();
    mdns_hostname_set(MDNS_HOST_NAME);
    mdns_instance_name_set(MDNS_INSTANCE);

    mdns_txt_item_t serviceTxtData[] = {
#ifdef CONFIG_IDF_TARGET_ESP32
        {"board", "esp32"},
#elif defined(CONFIG_IDF_TARGET_ESP32C2)
        {"board", "esp32-c2"},
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
        {"board", "esp32-c3"},
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
        {"board", "esp32-s2"},
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
        {"board", "esp32-s3"},
#endif
        {"path", "/"}};

    ESP_ERROR_CHECK(mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData,
                                     sizeof(serviceTxtData) / sizeof(serviceTxtData[0])));
}

esp_err_t init_fs(void)
{
    // wl_handle_t wl_handle;
    // const esp_vfs_fat_mount_config_t mount_config = {
    //     .format_if_mount_failed = true,
    //     .max_files = 3,
    //     .allocation_unit_size = CONFIG_WL_SECTOR_SIZE};
    // esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(MOUNT_FS_PATH, NULL, &mount_config, &wl_handle);
    // if (err != ESP_OK)
    // {
    //     ESP_LOGE(TAG, "Failed to mount or format filesystem");
    //     return err;
    // }
    // uint64_t total = 0, used = 0;
    // err = esp_vfs_fat_info(MOUNT_FS_PATH, &total, &used);
    // if (err != ESP_OK)
    //     ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(err));
    // else
    //     ESP_LOGI(TAG, MOUNT_FS_PATH " Partition size: total: %ld, free: %ld", (uint32_t)total, (uint32_t)used);

    esp_vfs_littlefs_conf_t conf = {
        .base_path = MOUNT_FS_PATH,
        .partition_label = "www",
        .format_if_mount_failed = true,
        .dont_mount = false,
    };

    esp_err_t ret = esp_vfs_littlefs_register(&conf);

    if (ret != ESP_OK)
    {
        if (ret == ESP_FAIL)
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        else if (ret == ESP_ERR_NOT_FOUND)
            ESP_LOGE(TAG, "Failed to find LittleFS partition");
        else
            ESP_LOGE(TAG, "Failed to initialize LittleFS (%s)", esp_err_to_name(ret));
        return ESP_FAIL;
    }

    size_t total = 0, used = 0;
    ret = esp_littlefs_info(conf.partition_label, &total, &used);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s)", esp_err_to_name(ret));
        esp_littlefs_format(conf.partition_label);
    }
    else
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    return ESP_OK;
}

#include "esp_sntp.h"
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 48
#endif
RTC_NOINIT_ATTR static uint8_t ntp_update;
RTC_NOINIT_ATTR static uint64_t ntp_timestamp;
void sntp_print_time(uint8_t type)
{
    char strftime_buf[32];
    time_t now = 0;
    struct tm timeinfo = {0};
    // Set timezone to Eastern Standard Time and print local time
    if (type)
    {
        setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1);
        tzset();
    }
    time(&now);
    localtime_r(&now, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%Y/%m/%d %H:%M:%S", &timeinfo);
    if (type)
        ESP_LOGI(TAG, "The current date/time in New York is: %s", strftime_buf);
    else
        ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf);
}
static void time_sync_notification_cb(struct timeval *tv)
{
    ntp_update = 1;
    ntp_timestamp = tv->tv_sec * 1000 + tv->tv_usec / 1000;
    ESP_LOGI(TAG, "Notification of a time synchronization event");
    sntp_print_time(0);
}
static void increase_tick(void *arg)
{
    ntp_timestamp++;
}

uint32_t sntp_get_timestamp(void)
{
    return ntp_timestamp / 1000;
}

void sntp_start(void)
{
    ESP_LOGI(TAG, "Initializing SNTP");
    esp_sntp_stop();
    esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);

#if SNTP_MAX_SERVERS > 0
    esp_sntp_setservername(0, "ntp1.aliyun.com");
    ESP_LOGI(TAG, "server %d: %s", 0, "ntp1.aliyun.com");
#endif
#if SNTP_MAX_SERVERS > 1
    esp_sntp_setservername(1, "210.72.145.44"); // 国家授时中心服务器 IP 地址
    ESP_LOGI(TAG, "server %d: %s", 1, "210.72.145.44");
#endif
#if SNTP_MAX_SERVERS > 2
    esp_sntp_setservername(2, "cn.pool.ntp.org"); // 中国区NTP服务的虚拟集群
    ESP_LOGI(TAG, "server %d: %s", 2, "cn.pool.ntp.org");
#endif
#if SNTP_MAX_SERVERS > 3
    esp_sntp_setservername(3, "pool.ntp.org");
    ESP_LOGI(TAG, "server %d: %s", 3, "pool.ntp.org");
#endif

    esp_sntp_set_time_sync_notification_cb(time_sync_notification_cb);
    esp_sntp_init();
}

#include "config.h"
#include "wifi.h"
#include "api_map.h"
#include "file_tool.h"

char client_id[32] = {0};
void app_main(void)
{
    esp_reset_reason_t reason = esp_reset_reason();
    if (reason == ESP_RST_POWERON)
    {
        ntp_update = 0;
        ntp_timestamp = 1735660800000;
    }

#if CONFIG_PM_ENABLE
    esp_pm_config_t pm_config = {
        .max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
        .min_freq_mhz = 40,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
        .light_sleep_enable = true
#endif
    };
    ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
#endif // CONFIG_PM_ENABLE

#if defined(CONFIG_IDF_TARGET_ESP32C3)
    ESP_LOGI(TAG, "System up %d", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
#endif
    gpio_install_isr_service(0);
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        // NVS 分区可能已损坏或需要格式化
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    extern void fdb_init(void);
    fdb_init();

    uint8_t mac[6] = {0};
    esp_base_mac_addr_get(mac);
    snprintf(client_id, sizeof(client_id), "ESP-BAT-PB_%02X%02X%02X", mac[3], mac[4], mac[5]);

    setenv("TZ", "CST-8", 1);
    tzset();
    /* 默认时间 : 2025-01-01 00:00:00 */
    struct timeval tv_now = {.tv_sec = 1735660800, .tv_usec = 0};
    if (ntp_update == 1)
    {
        /* 同步上次复位前的时间 */
        tv_now.tv_sec = ntp_timestamp / 1000;
        tv_now.tv_usec = (ntp_timestamp % 1000) * 1000;
        settimeofday(&tv_now, NULL);
        time_sync_notification_cb(&tv_now);
    }
    else
    {
        ntp_update = 0;
        settimeofday(&tv_now, NULL);
    }

    const esp_timer_create_args_t tick_timer_args = {
        .callback = &increase_tick,
        .name = "tick1ms"};
    esp_timer_handle_t tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&tick_timer_args, &tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(tick_timer, 1 * 1000));

    initialise_mdns();
    netbiosns_init();
    netbiosns_set_name(MDNS_HOST_NAME);
    init_fs();
    file_tool_add_mount(MOUNT_FS_PATH);
    printf("MD5: %s\r\n", WEB_INDEX_FILE_MD5);

    config_init("/www/config.d");
    api_auto_init();
    api_list();
    list_files("/", 2);

    // uint32_t tick = xTaskGetTickCount();

    vTaskDelay(pdMS_TO_TICKS(1000));
    // bool init_wifi = false;
    // if (!init_wifi && pdTICKS_TO_MS(xTaskGetTickCount() - tick) > 1000)
    // {
    //     init_wifi = true;
    wifi_init();
    // }

    vTaskDelay(pdMS_TO_TICKS(4000));
    // bool show_wakeup_source = false;
    // if (!show_wakeup_source && pdTICKS_TO_MS(xTaskGetTickCount() - tick) > 5000)
    // {
    //     show_wakeup_source = true;
    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    static const char *const wakeup_cause_str[] = {
        "UNDEFINED",
        "ALL",
        "EXT0",
        "EXT1",
        "TIMER",
        "TOUCHPAD",
        "ULP",
        "GPIO",
        "UART",
        "WIFI",
        "COCPU",
        "COCPU_TRAP_TRIG",
        "BT",
    };
    ESP_LOGW(TAG, "WAKEUP SOURCE (%d) %s", cause, wakeup_cause_str[cause]);
    ESP_LOGI(TAG, "Free RAM %d", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
    // }

#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
    vTaskDelay(pdMS_TO_TICKS(25000));
    // bool check_app = false;
    // if (!check_app && pdTICKS_TO_MS(xTaskGetTickCount() - tick) > 30000)
    // {
    //     check_app = true;
    const esp_partition_t *running = esp_ota_get_running_partition();
    esp_ota_img_states_t ota_state;
    if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
    {
        if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
        {
            esp_ota_mark_app_valid_cancel_rollback();
            ESP_LOGI(TAG, "upgrade is successful");
        }
    }
    // }
#endif
}
